<script lang="ts" setup>
import { ElMessage, ElMessageBox, PaginationProps, TableProps } from "element-plus"
import { computed, ComputedRef, h, reactive, ref, useAttrs } from "vue"
import { genKey, isFunc, pageSizeManager } from "../utils"
import { PageFetchAction, PageParams, KeyValues } from "../utils/types"
import UiSearchForm from './SearchForm.vue'

const UiCellRender = ({ render, params }) => params.$index > -1 && render(h, params)

const props = defineProps<{
  hidePager?: boolean,
  pagerOptions?: PaginationProps,
  columns?: any[],
  fetchAction: PageFetchAction
}>()

const cls = 'ui-data-view'
const form = ref<KeyValues>({})
const loading = ref(false)
const selectedRows = ref([])
const elTableRef = ref()
const attrs = useAttrs()
const initForm = ref({})

const listData = reactive({
  data: [],
  form: {},
  total: 0,
  params: { pageNum: 1, pageSize: pageSizeManager.pageSize }
})

const convertedColumns = computed(() => {
  return (props.columns || [])
    .map(c => ({ minWidth: 100, ...c, _key: genKey() }))
})

const tableProps: ComputedRef<TableProps<any>> = computed(() => {
  return {
    border: true,
    ...attrs,
    data: listData.data,
    onSelectionChange: (rows: any) => {
      selectedRows.value = rows
    }
  }
})

const pagerProps = computed(() => {
  return {
    pageSizes: [10, 20, 50],
    background: true,
    layout: 'total, sizes, prev, pager, next, jumper',
    ...(props.pagerOptions || {}),
    total: listData.total || 0
  }
}) as ComputedRef<PaginationProps>

const getData = async (params: PageParams = {}) => {
  loading.value = true
  selectedRows.value = []
  listData.params = { ...listData.params, ...params }
  try {
    const res = await props.fetchAction({ ...listData.form, ...listData.params })
    listData.data = res.rows || []
    listData.total = res.total || 0
  } catch (e) {
    listData.data = []
    listData.total = 0
  } finally {
    loading.value = false
  }
}

const onSearch = () => {
  listData.form = { ...form.value }
  getData({ pageNum: 1 })
}

const onReset = () => {
  form.value = { ...initForm.value }
  onSearch()
}

const isSizeChanged = ref(false)

const onSizeChange = (pageSize: number) => {
  isSizeChanged.value = true
  pageSizeManager.pageSize = pageSize
  getData({ pageSize, pageNum: 1 })
  setTimeout(() => isSizeChanged.value = false, 0)
}

const onCurrentChange = (pageNum: number) => {
  !isSizeChanged.value && getData({ pageNum })
}

type FormParamsAction = () => KeyValues | Promise<KeyValues>

const setInitForm = async (params: KeyValues | FormParamsAction) => {
  initForm.value = (isFunc(params) ? (await (params as FormParamsAction)()) : params) || {}
  form.value = { ...initForm.value }
  listData.form = { ...initForm.value }
}

type DeleteRowsAction = () => any | Promise<any>
type DeleteRowsOptions = {
  title?: string,
  desc?: string,
  action: DeleteRowsAction
}

const deleteRows = async (options: DeleteRowsAction | DeleteRowsOptions) => {
  if (isFunc(options)) {
    options = { action: options as DeleteRowsAction }
  }
  const { action, title, desc } = options as DeleteRowsOptions
  await ElMessageBox.confirm(
    desc || '确定删除选中的记录？',
    title || '删除',
    {
      type: 'warning',
      dangerouslyUseHTMLString: true
    }
  )
  await action()
  ElMessage.success('删除成功')
  await getData()
  const { params, data } = listData
  params.pageNum > 1 && data.length < 1 && getData({ pageNum: params.pageNum - 1 })
}

const onSaveSuccess = (isEdit?: any) => isEdit ? getData() : getData({ pageNum: 1 })

defineExpose({ getData, setInitForm, deleteRows, onSaveSuccess })
</script>

<template>
  <div :class="cls">
    <header :class="`${cls}_header`">
      <UiSearchForm v-show="$slots.search" @reset="onReset" @search="onSearch">
        <slot name="search" :form="form" :onSearch="onSearch" />
      </UiSearchForm>
      <div v-show="$slots.actions" :class="`${cls}_actions`">
        <slot name="actions" :form="form" :onSearch="onSearch" :selectedRows="selectedRows" />
      </div>
    </header>
    <main
      :class="`${cls}_main`"
      v-loading="loading"
      element-loading-background="rgba(255, 255, 255, .3)">
      <slot :data="listData.data">
        <ElTable ref="elTableRef" v-bind="tableProps">
          <ElTableColumn v-for="_ in convertedColumns" :key="_._key" v-bind="_">
            <template v-if="_.render" v-slot="params">
              <UiCellRender :render="_.render" :params="params" />
            </template>
          </ElTableColumn>
        </ElTable>
      </slot>
    </main>
    <footer :class="`${cls}_footer`">
      <ElPagination
        v-if="!hidePager"
        v-model:current-page="listData.params.pageNum"
        v-model:page-size="listData.params.pageSize"
        v-bind="pagerProps"
        @size-change="onSizeChange"
        @current-change="onCurrentChange" />
    </footer>
  </div>
</template>

<style lang="scss">
.ui-data-view {
  padding: $page-padding;
  background-color: #fff;
  height: 100%;
  display: flex;
  flex-direction: column;
  &_main {
    flex: 1;
    height: 0;
    .el-table {
      height: 100%;
    }
  }
  &_actions {
    margin-bottom: 15px;
  }
  &_actions {
    display: flex;
    align-items: center;
    > * + * {
      margin-left: 12px;
    }
  }
  &_footer {
    display: flex;
    justify-content: flex-end;
    margin-top: $page-padding;
    .el-pagination .el-select {
      width: 100px;
    }
  }
}
</style>
